// 模拟视图更新的
function updateView() {
  console.log("更新视图~");
}


const oldArrayProperty = Array.prototype;
// 创建一个对象，叫arrProto，它的原型对象是oldArrayProperty
const arrProto = Object.create(oldArrayProperty);
["push", "pop", "shift", "unshift", "splice"].forEach(methodName => {
  // 重写那7个方法
  arrProto[methodName] = function () {
    // 添加更新视图逻辑
    updateView();
    oldArrayProperty[methodName].call(this, ...arguments)
    // Array.prototype.push.call(this, ...arguments)
  }
})


// 重新定义属性，把属性都监听起来
function defineReactive(target, key, value) {

  // value是一个引用类型，需要深度监听
  observer(value)

  Object.defineProperty(target, key, {
    get() {
      console.log("get...");
      return value;
    },
    set(newValue) {
      if (newValue !== value) {
        // 深度监听
        observer(newValue)

        value = newValue;
        // 更新视图
        updateView()
      }
    }
  })
}

// 监听对象属性
function observer(target) {
  if (typeof target !== "object" || target === null) {
    // 不是对象或数组
    return target;
  }

  // 污染全局的Array原型上的方法
  // Array.prototype.push = function () {
  //   updateView()
  // }

  if (Array.isArray(target)) {
    target.__proto__ = arrProto;
  }

  // 重新定义各个属性，for in 遍历
  for (let key in target) {
    // console.log(key,target[key],target);
    defineReactive(target, key, target[key])
  }
}

let data = {
  name: "ml",
  age: 18,
  info: {
    address: "bj",
  },
  nums: [10, 20, 30]
}

// 监听数据
observer(data)

// 测试
// data.name = "wc"
// data.name = "ml"
// data.info = { qianming: "888" }
// data.x = 123; // 新增的属性没有响应式，监听不到的 --- Vue.set()  利用set可以新增一个具有响应式的数据
// delete data.name; // 删除属性，也是监听不到的 --- Vue.delete()
// data.info.address = "sh" // 深度监听
// console.log(data.nums[0])  // 这样写也监听到了
// console.log(data.nums[1])
// data.nums[0] = 666;  // // 这样写也监听到了
// data.num[38].xx = 112;

// 这个push不是原生的push,是yyx自己重写后的push
data.nums.push(4); // 在vue中调用了可以改变原数组的方法，是有响应式的
// 一共有7个方法：push pop shift unshift splice sort reverse

// 问：Object.defineProperty有什么缺点？
// 答：1）需要深度递归监听，只是深度递归，肯定有性能问题。
//    2）无法监听新增属性和删除属性 ，解决：Vue.set  Vue.delete
//    3）无法监听数据相关的方法，需要特殊处理。
//    在vue3中，响应式就没有使用Object.defineProperty，使用了proxy。































